package com.ytrsoft.ws;

import com.ytrsoft.bean.Message;
import com.ytrsoft.task.ActionFactory;
import com.ytrsoft.task.ActionHandler;
import com.ytrsoft.utils.MsgKit;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class AbstractSocketService<T extends BaseSocket> {

    protected final AtomicInteger connectionCount = new AtomicInteger(0);
    private final Map<String, T> caches = new ConcurrentHashMap<>();
    private final Map<String, List<ActionHandler>> handles = new ConcurrentHashMap<>();

    protected abstract T createSocket();
    protected abstract ActionFactory<T> getActionFactory(T socket);

    protected void handleOpen(T socket) {}
    protected void handleClose(T socket) {}
    protected void handleError(T socket) {}

    protected void handleMessage(T socket, Message message) {
        ActionFactory<T> factory = getActionFactory(socket);
        ActionHandler handler = factory.createHandler(message.getAction());
        handles.get(socket.getId()).add(handler);
        handler.doMessage(message);
    }

    protected void addHandler(String id, ActionHandler handler) {
        handles.get(id).add(handler);
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("id") String id) {
        T socket = createSocket();
        socket.setId(id);
        socket.setSession(session);
        caches.put(id, socket);
        handles.put(id, new CopyOnWriteArrayList<>());
        handleOpen(socket);
        connectionCount.incrementAndGet();
    }

    @OnMessage
    public void onMessage(String payload, @PathParam("id") String id) {
        T socket = caches.get(id);
        Message message = Message.fromJSON(payload);
        handleMessage(socket, message);
    }

    @OnClose
    public void onClose(@PathParam("id") String id) {
        T socket = caches.get(id);
        handleClose(socket);
        caches.remove(id);
        List<ActionHandler> handlerlist = handles.get(id);
        handlerlist.forEach(ActionHandler::destroy);
        connectionCount.decrementAndGet();
    }

    @OnError
    public void onError(Throwable t, @PathParam("id") String id) {
        T socket = caches.get(id);
        Message message = MsgKit.create(t);
        socket.postMessage(message);
        handleError(socket);
    }
}
